package com.supplychain.scm.core.feign.config;


import com.supplychain.scm.core.basis.constant.StringPool;
import com.supplychain.scm.core.feign.annotation.EnableDbySupplyFeign;
import com.supplychain.scm.core.feign.annotation.EnableGrayFeign;
import com.supplychain.scm.core.feign.constant.SupplyFeignConstants;
import com.supplychain.scm.core.feign.interceptor.AbstractGrayFeignInterceptor;
import com.supplychain.scm.core.feign.interceptor.AbstractSupplyFeignInterceptor;
import com.google.common.collect.Lists;
import com.supplychain.scm.core.basis.constant.CommonConstants;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Modifier;
import java.util.*;

/**
 * @author liyang
 * @version 1.0.0
 * @ClassName SupplyFeignInterceptorRegistrar
 * @createTime 12:18:00 2022/08/18
 */
@Slf4j
public class SupplyFeignInterceptorRegistrar implements ImportBeanDefinitionRegistrar {

	@SneakyThrows
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
		AnnotationAttributes enableDbySupplyInterceptor = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableDbySupplyFeign.class.getName()));
		String[] scanePaths = {SupplyFeignConstants.DEFAULT_SCANE_PATH};
		String bizCode = StringPool.EMPTY;
		if(Objects.nonNull(enableDbySupplyInterceptor)) {
			bizCode = Optional.ofNullable(enableDbySupplyInterceptor).map(e -> e.getString("bizCode")).orElse(bizCode).trim().toLowerCase(Locale.ROOT);
			scanePaths = Optional.ofNullable(enableDbySupplyInterceptor).map(e -> e.getStringArray("scanePath")).orElse(scanePaths);
		} else {
			AnnotationAttributes enableGrayFeignInterceptor = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableGrayFeign.class.getName()));
			scanePaths = Optional.ofNullable(enableGrayFeignInterceptor).map(e -> e.getStringArray("scanePath")).orElse(scanePaths);
		}
		List<Class<AbstractGrayFeignInterceptor>> subClasses = this.getSubClasses(AbstractGrayFeignInterceptor.class, scanePaths);
		if (CollectionUtils.isEmpty(subClasses)) {
			return;
		}
		for (Class<AbstractGrayFeignInterceptor> subClass : subClasses) {
			String className = this.lowerFirstChar(subClass.getSimpleName());
			if (!registry.containsBeanDefinition(className)) {
				GenericBeanDefinition propertyControllerBeanDefinition = (GenericBeanDefinition) BeanDefinitionBuilder
						.genericBeanDefinition(subClass)
						.getBeanDefinition();
				if(AbstractSupplyFeignInterceptor.class.isAssignableFrom(subClass)){
					propertyControllerBeanDefinition.getPropertyValues().add("bizCode", bizCode);
				}
				registry.registerBeanDefinition(className, propertyControllerBeanDefinition);
			}
		}
	}

	private <E> List<Class<E>> getSubClasses(final Class<E> parentClass, final String[] packagePaths) throws ClassNotFoundException {
		final List<Class<E>> subClasses = Lists.newArrayList();
		if(!ArrayUtils.isEmpty(packagePaths)) {
			final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
			provider.addIncludeFilter(new AssignableTypeFilter(parentClass));
			for (String packagePath : packagePaths) {
				final Set<BeanDefinition> components = provider.findCandidateComponents(packagePath);
				for (final BeanDefinition component : components) {
					@SuppressWarnings("unchecked") final Class<E> cls = (Class<E>) Class.forName(component.getBeanClassName());
					if (Modifier.isAbstract(cls.getModifiers()) || Modifier.isInterface(cls.getModifiers())) {
						continue;
					}
					subClasses.add(cls);
				}
			}
		}
		return subClasses;
	}

	private String lowerFirstChar(String name){
		if(StringUtils.isBlank(name)) {
			return StringPool.EMPTY;
		}
		char[] chars = name.toCharArray();
		chars[CommonConstants.ZERO] += 32;
		return String.valueOf(chars);
	}
}
